Release 10.1A: OpenEdge Development:
Progress Dynamics Advanced Development


Disabling actions based on a data value

For example, suppose that you want users of the oemaintwin window you built to be able to edit or delete only Customers and Orders for Customers in the USA. To do this, you must disable the buttons that allow those operations, depending on the value of the currently selected Customer, and enable or disable the fields in the viewers on the different folder pages accordingly.

Using the TableIOType property

The first thing you must do is to change the basic display mode of the toolbar to leave viewers disabled until someone specifically wants to do an update. That way, the viewers are initially disabled, and the user must have access to the Modify button to make changes.

In earlier versions of the ADM and its SmartPanels, this was done with the PanelType property, which you could set to one of two states:

The toolbar equivalent of this property is TableIOType, and it has the same two possible values: Save and Update. The default setting is Save, so things start out enabled. You must change the setting to Update for this example to work. Change the value in the Container Builder, by bringing up the toolbar’s dynamic property sheet, as shown in Figure 3–9, and changing the property value there.

Figure 3–9: Dynamic Properties window

There is another way to make run-time changes to the object in a custom super procedure that executes custom code when the window is initialized. You can define a custom super procedure for the toolbar itself, but because this must be done as part of the overall window initialization, this example shows the code in a custom super procedure for the container window. Where you put your code is a matter of overall organization and preference; there is always more than one solution to a programming problem.

Defining a custom super procedure for the window

Define a New Structured Procedure in the AppBuilder and create an initializeObject internal procedure for it. Because this code is executed on behalf of the window, it first must get the handle of the Toolbar object. To do this, add a ContainerToolbar link from the toolbar to the window (THIS-OBJECT), which might be useful for other purposes as well, as is described in a later section of this chapter. Alternatively, the code could simply check among the window’s Container-Targets for the handle of an object with the ObjectName of FolderPageTop. Again, there is almost always more than one way to solve a problem.

Given the toolbar handle, you then must set the TableIOType property to Update. This code sample uses the {get} and {set} include files to do the job. You could also use the DYNAMIC-FUNCTION syntax and the equivalent get and set functions for the same effect. This is also mostly a matter of personal preference and coding style. This all must happen before the RUN SUPER statement so that the property is set before all the objects are initialized, as shown in the following code:

Procedure initializeObject: 
/*------------------------------------------------------------------------- 
  Purpose:     Override of initializeObject to reset the TableIOType  
               property value to 'update' so that Viewers are initially 
               disabled. 
  Parameters:  <none>  
-------------------------------------------------------------------------*/ 
DEFINE VARIABLE hToolbar AS HANDLE     NO-UNDO. 
  {get ContainerToolbarSource hToolbar}. 
  {set TableIoType 'update' hToolbar}. 
  RUN SUPER. 
END PROCEDURE. 

Registering the custom super procedure

As always, remember to register your super procedure in the Repository by selecting that option in the AppBuilder’s File menu. Save it as a Procedure in the appropriate product module. We have called the procedure oemaintwinsuper.p.

Once you have done this, attach it as the custom super procedure for the container window. You can do this in the Container Builder, as shown in Figure 3–10.

Figure 3–10: Container Builder for oemaintwin

Note that you can set the custom super procedure for objects that are edited in the AppBuilder, such as browsers and viewers, in the AppBuilder’s property sheets for those objects.

As mentioned elsewhere, you do not specify the relative pathname where the procedure is actually stored because you are just providing its LogicalObjectName as stored in the Repository. This name includes the .p filename extension but not the pathname. The pathname is attached automatically at run time by looking up the product module record in the Repository.

Defining a custom super procedure for a viewer

Now that you have set the TableIOType property, you must create code to selectively enable and disable both the toolbar buttons and the viewer fields depending on the value of the Customer Country field. Put this code into a custom super procedure for the custcommentsv viewer that we have named customersuper.p. See the "Defining a custom super procedure for the window" section for more information on how to create this procedure.

Using the modifyDisabledActions procedure In the toolbar

To control the toolbar buttons and associated menu items, change the DisabledActions or HiddenActions attributes. In this example, you should disable the buttons and menu items rather than hide them so that they do not appear and disappear as the user changes records, which would not be an appropriate user interface. In other situations you can remove the button or menu item from the toolbar altogether by adding it to the HiddenActions property.

There is a special support procedure to help you add and remove entries from the DisabledActions property, whose value is a comma-separated list of actions. Note that at present there is no equivalent procedure for HiddenActions; you will have to parse the list yourself or create your own modifyHiddenActions procedure based on modifyDisabledActions.

First you must identify the names of the actions that you are going to disable. To identify these names, open the toolbar in the Toolbar and Menu Designer. Expand the SmartToolbars node and locate the toolbar you are using, which is FolderPageTop in this case. Expand that node to see the bands it uses, and then expand the TableIoMod band, which holds all the update-related actions. Here you can see the description of each action.

Following the description is the actual action name in parentheses, which also appears as the Item Reference in the maintenance frame to the right, as shown in Figure 3–11. This is the name to use in setting DisabledActions.

Figure 3–11: Toolbar and Menu designer

To disable the Delete button in the FolderPageTop toolbar, and also the two buttons that allow you to alternate the state of the displayed record from View mode to Update mode, you must add Delete, FolderView, and FolderUpdate to the list.

Since you need to do this each time a Customer is selected, the appropriate place for the code is in a local version of the dataAvailable event procedure. This is published by the SDO each time the record position changes and the viewer subscribes to the event so that it can display the new values. Unlike most event procedures, which have no parameters, this one has a single INPUT parameter that you must define and pass on to the super procedure. See Chapter 5, "Using ADM2 Properties and Methods in Progress Dynamics," for more information on this and many other useful properties and entry points in the code that you might want to use.

Important note about parameters on overrides and publish

Remember to double-check for parameters in any procedure that you are going to override and in any event that you publish. If you get the parameter list wrong for a local version of a procedure that you RUN, you will get an error at run time to remind you of your mistake. But if you get the calling sequence wrong for a PUBLISH statement, the event simply will not occur because there will not be any matching subscriber with the same parameter list.

Unfortunately, when you create a custom super procedure and add code to it in the AppBuilder, the Section Editor is not aware of what object it will be associated with, so it cannot supply you with a list of all the valid procedures and functions you can override, along with their parameters, as it does when you add procedures to a static SmartObject. Therefore, make sure you get this right. Also, do not forget to include the RUN SUPER statement in the right place in your custom code. If you leave it out, the standard code for the event will not execute at all, and the results will likely be very strange.

Getting values from different linked objects

Your custom code must first get the handle of the toolbar. Because there is no link from the toolbar to the Comments viewer (the viewer is not updateable, so it has no TableIO link), you must get the handle of the Customer SDO, which is the viewer’s Data-Source, and then get the SDO’s Navigation-Source, which is the toolbar.

You also need to retrieve the value of the Country field for the selected record. You can use the columnValue function in the SDO to do this.

And finally, the code must get a list of all the TableIO-Targets of the toolbar (which are the three updateable viewers on Pages 2, 3, and 4). This is the TableIOTarget property. Though the name is singular (target not targets), the data type is CHARACTER, because there might be a list of handles of multiple targets, stored as a comma-separated list of handles in string form, as shown in the following:

Procedure dataAvailable: 
/*------------------------------------------------------------------------- 
  Purpose:    Override of dataAvailable to reset the DisabledActions of 
              the toolbar and enabled or disable fields based on the Country.  
  Parameters:  pcRelative AS CHARACTER 
  Notes:        
-------------------------------------------------------------------------*/ 
DEFINE INPUT  PARAMETER pcRelative AS CHARACTER  NO-UNDO. 
DEFINE VARIABLE cCountry    AS CHARACTER  NO-UNDO. 
DEFINE VARIABLE hDataSource AS HANDLE     NO-UNDO. 
DEFINE VARIABLE hToolbar    AS HANDLE     NO-UNDO. 
DEFINE VARIABLE cTargets    AS CHARACTER  NO-UNDO. 
DEFINE VARIABLE hTarget     AS HANDLE     NO-UNDO. 
DEFINE VARIABLE iTarget     AS INTEGER    NO-UNDO. 
ASSIGN hDataSource = DYNAMIC-FUNCTION ('getDataSource' IN TARGET-PROCEDURE) 
       hToolbar    = DYNAMIC-FUNCTION ('getNavigationSource' IN hDataSource) 
       cCountry    = DYNAMIC-FUNCTION ('columnValue' IN hDataSource,'Country') 
       cTargets    = DYNAMIC-FUNCTION ('getTableIOTarget' IN hToolbar). 

Next, the code checks the value of the Country field and runs modifyDisabledActions to either add or remove the actions. Remember that the goal is to allow users to edit and delete only records related to customers in the USA. The modifyDisabledActions procedure takes two arguments:

If the country is USA, the user is allowed to do updates and deletes, so the actions are removed from the disabled list, as shown:

IF cCountry EQ "USA" THEN 
DO: 
    DYNAMIC-FUNCTION ('modifyDisabledActions' IN hToolbar, 
                     'Remove', 'FolderUpdate,FolderView,Delete'). 

Using enableFields and disableFields

Then the code goes through the list of TableIO-Targets, converts each back to a handle, and runs the enableFields procedure in it. In order to avoid doing this unnecessarily, it first checks the value of the viewer’s FieldsEnabled property to see whether the fields are already in the right state, as shown:

 DO iTarget = 1 TO NUM-ENTRIES(cTargets): 
        hTarget = WIDGET-HANDLE(ENTRY(iTarget, cTargets)). 
        IF NOT DYNAMIC-FUNCTION('getFieldsEnabled' IN hTarget) THEN 
            RUN enableFields IN hTarget. 
    END. 
END. 

Correspondingly, if the country is not equal to USA, then the actions are added to the disabled list and the code must run the disableFields procedure in the viewer. This is another case where you have to be careful to get the calling sequence right. Unlike enableFields, the disableFields procedure takes an INPUT parameter, which can be All or Create to indicate whether all fields are to be disabled, or just those associated with creating a new record, as shown:

ELSE DO: 
    DYNAMIC-FUNCTION ('modifyDisabledActions' IN hToolbar, 
                     'Add',    'FolderUpdate,FolderView,Delete').   
    DO iTarget = 1 TO NUM-ENTRIES(cTargets): 
        hTarget = WIDGET-HANDLE(ENTRY(iTarget, cTargets)). 
        IF DYNAMIC-FUNCTION('getFieldsEnabled' IN hTarget) THEN 
            RUN disableFields IN hTarget ('All'). 
    END. 
END. 

This is the end of the code for the viewer’s localization of dataAvailable. Be sure to register the super procedure in the Repository and associate it with the viewer. You can set the custom super procedure for the viewer in the Repository Maintenance tool, or more conveniently in the AppBuilder, by opening the viewer and going into its property sheet, as shown in Figure 3–12.

Figure 3–12: Property sheet for frMain

Testing the window with DisabledActions

Now, when you run the application window and select a customer not in the USA, all the viewers are disabled. The Delete, View, and Modify actions in the toolbar are also disabled, as shown in Figure 3–13.

Figure 3–13: Test of window wIth DisabledActions

Using the toolbar’s reset procedures

The toolbar uses a set of event procedures to reset the action state for various types of actions. These procedures are executed when it seems correct to check for a state change. As a result, you do not normally need to run them yourself. For instance, they are run on a page change, which is why the action state is corrected when you switch pages.

The event procedures that the toolbar supports are resetTableIo, resetCommit, and resetNavigation. These are published by objects like the viewer, and generally you do not need to invoke them explicitly. If you find that the toolbar state is not being reset properly at some particular place in your application, such as this viewer, you can simply publish the event from the viewer and the toolbar will receive it.

Remember to publish it FROM TARGET-PROCEDURE so that the event is associated with the viewer itself and not its super procedure. This is what the first block of code looks like with an example of publishing resetTableIo to make sure that the update buttons are reset properly:

IF cCountry EQ "USA" THEN 
DO: 
    DYNAMIC-FUNCTION ('modifyDisabledActions' IN hToolbar, 
                     'Remove', 'FolderUpdate,FolderView,Delete'). 
    PUBLISH 'resetTableIO' FROM TARGET-PROCEDURE. 
    DO iTarget = 1 TO NUM-ENTRIES(cTargets): 
        hTarget = WIDGET-HANDLE(ENTRY(iTarget, cTargets)). 
        IF NOT DYNAMIC-FUNCTION('getFieldsEnabled' IN hTarget) THEN 
            RUN enableFields IN hTarget. 
    END. 
END. 

You can also publish the general-purpose reset procedure resetTargetActions to reset actions related to other link types, including custom links of your own. The procedure takes the name of the link as an INPUT parameter.


Copyright © 2005 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095